package com.hifar.utils;

import MvCameraControlWrapper.CameraControlException;
import MvCameraControlWrapper.MvCameraControl;
import com.tssk.kylx.fw.utils.exception.BusinessException;
import com.tssk.kylx.fw.utils.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

import static MvCameraControlWrapper.MvCameraControl.MV_CC_EnumDevices;
import static MvCameraControlWrapper.MvCameraControlDefines.*;
import static MvCameraControlWrapper.MvCameraControlDefines.MV_RECORD_FORMAT_TYPE.MV_FormatType_AVI;

public class CameraUtils {

    private static Logger logger = LoggerFactory.getLogger(CameraUtils.class);

    private static ConcurrentHashMap<String, RecordThread> handleMap = new ConcurrentHashMap<>();
    private static final String TEMP_FILE_PATH = "D:\\temp";
    private static ExecutorService executorService = new ThreadPoolExecutor(4, 8, 0L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5));

    //临时实现用，正式使用需走配置
    private static final String SERVER_IP_ADDR = "127.0.0.1";

    public static void main(String[] args) throws Exception {
//        String ipAddr = "192.168.3.101";
//        String ipAddr = "192.168.130.21";

//        String[] ip = ipAddr.split("\\.");
//        long currentIp = (Long.parseLong(ip[0]) << 24) + (Long.parseLong(ip[1]) << 16) + (Long.parseLong(ip[2]) << 8) + Long.parseLong(ip[3]);
//        System.out.println(currentIp);

        long longIp = -1062726345;
        String ss = String.format("%d.%d.%d.%d",
                (longIp >> 24) & 0xff,
                (longIp >> 16) & 0xff,
                (longIp >> 8) & 0xff,
                longIp & 0xff);
        System.out.println("ss:  " + ss);

        long longIp1 = -1062698475;
        String ss1 = String.format("%d.%d.%d.%d",
                (longIp1 >> 24) & 0xff,
                (longIp1 >> 16) & 0xff,
                (longIp1 >> 8) & 0xff,
                longIp1 & 0xff);
        System.out.println("ss1:  " + ss1);
//        CameraUtils.saveImageByIp("192.168.3.101", "linshi111");

        CameraUtils.addWatermark("C:\\Users\\dell\\Pictures\\myPicture\\R - 222.jpg", "jpg", "华天海峰");
//        CameraUtils.openDevice(ipAddr);
//        CameraUtils.startGrabbing(ipAddr);
//        CameraUtils.startSaveVideoByIp(ipAddr, "cehsi123");
//
//        try {
//            Thread.sleep(10000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//
//        CameraUtils.saveImageByIp(ipAddr, "linshi222");
//
//        try {
//            Thread.sleep(10000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//
//        CameraUtils.stopSaveVideoByIp(ipAddr);
    }


    // 打开设备并开始拉去摄像头数据
    public static void openDevice(String ipAddress) {
        int nRet = MV_OK;
        Handle hCamera = null;

        RecordThread recordThread = handleMap.get(ipAddress);

        if (recordThread != null  && recordThread.gethCamera() != null) {
            return;
        }

        MV_CC_DEVICE_INFO ccDeviceInfo = getMvCcDeviceInfo(ipAddress, SERVER_IP_ADDR);

        // Create handle
        try {
            hCamera = MvCameraControl.MV_CC_CreateHandle(ccDeviceInfo);
        } catch (CameraControlException e) {
            logger.error("Create handle failed!" + e.toString());
            e.printStackTrace();
            hCamera = null;
            throw new BusinessException("连接设备失败");
        }

        // Open device
        nRet = MvCameraControl.MV_CC_OpenDevice(hCamera);
        if (MV_OK != nRet) {
            logger.error("Connect to camera failed, errcode: [{}]\n", Integer.toHexString(nRet));
            throw new BusinessException("连接设备失败");
        }

        // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)
        int nPacketSize = 0;
        if (ccDeviceInfo.transportLayerType == MV_GIGE_DEVICE) {
            nPacketSize = MvCameraControl.MV_CC_GetOptimalPacketSize(hCamera);
            if (nPacketSize > 0) {
                nRet = MvCameraControl.MV_CC_SetIntValue(hCamera, "GevSCPSPacketSize", nPacketSize);
                if (MV_OK != nRet) {
                    logger.error("Warning: Set Packet Size fail! , errcode: [{}]\n", Integer.toHexString(nRet));
                    throw new BusinessException("连接设备失败");
                }
            } else {
                logger.error("Warning: Get Packet Size fail!, errcode: [{}]\n", Integer.toHexString(nRet));
                throw new BusinessException("连接设备失败");
            }

        }

        nRet = MvCameraControl.MV_CC_GetBoolValue(hCamera, "AcquisitionFrameRateEnable", Boolean.FALSE);
        if (MV_OK != nRet) {
            logger.error("get AcquisitionFrameRateEnable failed, errcode: [{}]\n", Integer.toHexString(nRet));
            throw new BusinessException("连接设备失败");
        }

        // Make sure that trigger mode is off
        nRet = MvCameraControl.MV_CC_SetEnumValueByString(hCamera, "TriggerMode", "Off");
        if (MV_OK != nRet) {
            logger.error("SetTriggerMode failed, errcode: [{}]\n", Integer.toHexString(nRet));
            throw new BusinessException("连接设备失败");
        }

        // 开始取流
        nRet = MvCameraControl.MV_CC_StartGrabbing(hCamera);
        if (MV_OK != nRet) {
            logger.error("Start Grabbing fail, errcode: [{}]\n", Integer.toHexString(nRet));
            throw new BusinessException("连接设备失败");
        }


        RecordThread thread = new RecordThread();
        thread.sethCamera(hCamera);

        handleMap.put(ipAddress, thread);
    }

    /**
     * 开始采集数据
     *
     * @param ipAddress
     */
    public static void startGrabbing(String ipAddress) {

        RecordThread recordThread = handleMap.get(ipAddress);

        if (recordThread == null  && recordThread.gethCamera() != null) {
            throw new BusinessException("请先打开设备");
        }

//        Handle hCamera = recordThread.gethCamera();

        if (!recordThread.getIsGrabbing().get()) {
            recordThread.setIsGrabbing(Boolean.TRUE);
            // 启动线程采集数据
            executorService.execute(recordThread);
        }
    }


    /**
     * 停止采集数据
     *
     * @param ipAddress
     */
    public static void stopGrabbing(String ipAddress) {

        RecordThread recordThread = handleMap.get(ipAddress);

        if (recordThread == null  && recordThread.gethCamera() != null) {
            throw new BusinessException("请先打开设备");
        }

        Handle hCamera = recordThread.gethCamera();

        do {
            // Stop grabbing
            int nRet = MvCameraControl.MV_CC_StopGrabbing(hCamera);
            if (MV_OK != nRet) {
                System.err.printf("StopGrabbing fail, errcode: [%#x]\n", nRet);
                break;
            }
        } while (false);

        recordThread.setIsGrabbing(Boolean.FALSE);

        recordThread.interrupt();

    }

    // 停止拉流 并关闭摄像头
    public static void closeDevice(String ipAddress) {

        RecordThread recordThread = handleMap.get(ipAddress);

        if (recordThread == null  && recordThread.gethCamera() != null) {
            System.err.println("请先打开设备！");
            return;
        }

        Handle hCamera = recordThread.gethCamera();

        int nRet;
        do {
            // 关闭设备
            nRet = MvCameraControl.MV_CC_CloseDevice(hCamera);
            if (MV_OK != nRet) {
                logger.error("CloseDevice fail, errcode: [{}]\n", Integer.toHexString(nRet));
            }
        } while (false);

        if (null != hCamera) {
            // Destroy handle
            nRet = MvCameraControl.MV_CC_DestroyHandle(hCamera);
            if (MV_OK != nRet) {
                logger.error("DestroyHandle failed, errcode: [{}]\n", Integer.toHexString(nRet));
            }
            handleMap.remove(ipAddress);
        }

//        executorService.shutdown();

    }

    /**
     * 录像
     *
     * @param ipAddress
     * @param fileName
     */
    public static void startSaveVideoByIp(String ipAddress, String fileName) {
        CameraUtils.startSaveVideoByIp(ipAddress, fileName, TEMP_FILE_PATH);
    }


    public static RecordThread saveVideoStart(String ipAddress, String fileName, String filePath, String serverIpAddr) {

        int nRet = MV_OK;
        Handle hCamera = null;
        MV_CC_DEVICE_INFO mvCcDeviceInfo = getMvCcDeviceInfo(ipAddress, serverIpAddr);

        // Create handle
        try {
            hCamera = MvCameraControl.MV_CC_CreateHandle(mvCcDeviceInfo);
        } catch (CameraControlException e) {
            logger.error("Create handle failed!" + e.toString());
            e.printStackTrace();
            hCamera = null;
            throw new BusinessException("连接设备失败");
        }

        // Open device
        nRet = MvCameraControl.MV_CC_OpenDevice(hCamera);
        if (MV_OK != nRet) {
            logger.error("Connect to camera failed, errcode: [{}]\n", Integer.toHexString(nRet));
            throw new BusinessException("连接设备失败");
        }

        // Make sure that trigger mode is off
        nRet = MvCameraControl.MV_CC_SetEnumValueByString(hCamera, "TriggerMode", "Off");
        if (MV_OK != nRet) {
            logger.error("SetTriggerMode failed, errcode: [{}]\n", Integer.toHexString(nRet));
            throw new BusinessException("连接设备失败");
        }

        MVCC_INTVALUE stParam = new MVCC_INTVALUE();
        MV_CC_RECORD_PARAM stSaveParam = new MV_CC_RECORD_PARAM();
        // ch:获取图像宽度 | en:Get the width of the image
        nRet = MvCameraControl.MV_CC_GetIntValue(hCamera, "Width", stParam);
        if (MV_OK != nRet) {
            logger.error("get width fail!, errcode: [{}]\n", Integer.toHexString(nRet));
            throw new BusinessException("录像失败");
        }
        stSaveParam.nWidth = (short) stParam.curValue;

        nRet = MvCameraControl.MV_CC_GetIntValue(hCamera, "Height", stParam);
        if (MV_OK != nRet) {
            logger.error("get Height fail!, errcode: [{}]\n", Integer.toHexString(nRet));
            throw new BusinessException("录像失败");
        }
        stSaveParam.nHeight = (short) stParam.curValue;

        // 获得像素
        MVCC_ENUMVALUE stEnumParam = new MVCC_ENUMVALUE();
        nRet = MvCameraControl.MV_CC_GetEnumValue(hCamera, "PixelFormat", stEnumParam);
        if (MV_OK != nRet) {
            logger.error("get PixelFormat fail!, errcode: [{}]\n", Integer.toHexString(nRet));
            throw new BusinessException("录像失败");
        }
        MvGvspPixelType pixelType = null;
        for (MvGvspPixelType value : MvGvspPixelType.values()) {
            if (value.getnValue() == stEnumParam.curValue) {
                pixelType = value;
                break;
            }
        }
        if (pixelType != null) {
            stSaveParam.enPixelType = pixelType;
        }

        // 获取图像帧率
        MVCC_FLOATVALUE stFloatParam = new MVCC_FLOATVALUE();
        nRet = MvCameraControl.MV_CC_GetFloatValue(hCamera, "ResultingFrameRate", stFloatParam);
        if (MV_OK != nRet) {
            logger.error("get ResultingFrameRate value fail!, errcode: [{}]\n", Integer.toHexString(nRet));
            throw new BusinessException("录像失败");
        }
        stSaveParam.fFrameRate = stFloatParam.curValue;

        // ch:录像结构体赋值
        stSaveParam.nBitRate = 1000;
        stSaveParam.enRecordFmtType = MV_FormatType_AVI;
        stSaveParam.strFilePath = filePath + File.separator + String.format("%s.avi", fileName);

        // 开始录像
        nRet = MvCameraControl.MV_CC_StartRecord(hCamera, stSaveParam);
        if (MV_OK != nRet) {
            logger.error("Start Record fail!, errcode: [{}]\n", Integer.toHexString(nRet));
            throw new BusinessException("录像失败");
        }

        // 开始取流
        nRet = MvCameraControl.MV_CC_StartGrabbing(hCamera);
        if (MV_OK != nRet) {
            logger.error("Start Grabbing fail, errcode: [{}]\n", Integer.toHexString(nRet));
            throw new BusinessException("连接设备失败");
        }

        // 打开设备了 记录一下
        RecordThread thread = new RecordThread();
        thread.sethCamera(hCamera);
        thread.setIsRecording(Boolean.TRUE);
        thread.start();

        thread.plusActiveCount();

        handleMap.put(ipAddress, thread);

        return thread;
    }

    private static MV_CC_DEVICE_INFO getMvCcDeviceInfo(String ipAddress, String serverIpAddr) {
        MV_CC_DEVICE_INFO stDevInfo = new MV_CC_DEVICE_INFO();

        logger.info("SDK Version " + MvCameraControl.MV_CC_GetSDKVersion());

        try {
            List<MV_CC_DEVICE_INFO> stDeviceList = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE);
            if (stDeviceList.size() <= 0) {
                logger.error("枚举相机时，未查询到任何设备， No devices found!");
            } else {
                logger.info("开始打印枚举到的相机===============================");
                for (MV_CC_DEVICE_INFO info : stDeviceList) {
                    printDeviceInfo(info);
                }
                logger.info("结束打印枚举到的相机===============================");
            }
        } catch (CameraControlException e) {
            logger.error("Enumrate devices failed!" + e.toString());
            e.printStackTrace();
        }

        if (StringUtils.isBlank(ipAddress) || StringUtils.isBlank(serverIpAddr)) {
            throw new BusinessException("请检查ip地址配置！");
        }

        stDevInfo.transportLayerType = MV_GIGE_DEVICE;
        stDevInfo.gigEInfo.currentIp = ipAddress;
        stDevInfo.gigEInfo.netExport = serverIpAddr;

        printDeviceInfo(stDevInfo);
        return stDevInfo;
    }

    public static void saveVideoStop(String ipAddr, RecordThread recordThread) {

        Handle hCamera = recordThread.gethCamera();
        // Stop 录像
        int nRet = MvCameraControl.MV_CC_StopRecord(hCamera);
        if (MV_OK != nRet) {
            logger.error("StopRecord fail, errcode: [{}]\n", Integer.toHexString(nRet));
        }

        recordThread.setIsRecording(Boolean.FALSE);
        recordThread.interrupt();

        // 停止取流
        nRet = MvCameraControl.MV_CC_StopGrabbing(hCamera);
        if (MV_OK != nRet) {
            System.err.printf("StopGrabbing fail, errcode: [%#x]\n", nRet);
        }

        nRet = MvCameraControl.MV_CC_CloseDevice(hCamera);
        if (MV_OK != nRet) {
            logger.error("CloseDevice fail, errcode: [{}]\n", Integer.toHexString(nRet));
        }

        if (null != hCamera) {
            // Destroy handle
            nRet = MvCameraControl.MV_CC_DestroyHandle(hCamera);
            if (MV_OK != nRet) {
                logger.error("DestroyHandle failed, errcode: [{}]\n", Integer.toHexString(nRet));
            }
            if (recordThread != null) {
                Integer activeCount = recordThread.minusActiveCount();
                if (activeCount <= 0) {
                    handleMap.remove(ipAddr);
                }
            }
        }

    }

    /**
     * 录像
     *
     * @param ipAddress
     * @param fileName
     */
    public static void startSaveVideoByIp(String ipAddress, String fileName, String filePath) {

        RecordThread recordThread = handleMap.get(ipAddress);

        if (recordThread == null  && recordThread.gethCamera() != null) {
            logger.error("请先打开设备！");
            throw new BusinessException("请先打开设备");
        }

        Handle hCamera = recordThread.gethCamera();

        MVCC_INTVALUE stParam = new MVCC_INTVALUE();
        MV_CC_RECORD_PARAM stSaveParam = new MV_CC_RECORD_PARAM();
        // ch:获取图像宽度 | en:Get the width of the image
        int nRet = MvCameraControl.MV_CC_GetIntValue(hCamera, "Width", stParam);
        if (MV_OK != nRet) {
            logger.error("get width fail!, errcode: [{}]\n", Integer.toHexString(nRet));
            throw new BusinessException("录像失败");
        }
        stSaveParam.nWidth = (short) stParam.curValue;

        nRet = MvCameraControl.MV_CC_GetIntValue(hCamera, "Height", stParam);
        if (MV_OK != nRet) {
            logger.error("get Height fail!, errcode: [{}]\n", Integer.toHexString(nRet));
            throw new BusinessException("录像失败");
        }
        stSaveParam.nHeight = (short) stParam.curValue;

        // 获得像素
        MVCC_ENUMVALUE stEnumParam = new MVCC_ENUMVALUE();
        nRet = MvCameraControl.MV_CC_GetEnumValue(hCamera, "PixelFormat", stEnumParam);
        if (MV_OK != nRet) {
            logger.error("get PixelFormat fail!, errcode: [{}]\n", Integer.toHexString(nRet));
            throw new BusinessException("录像失败");
        }
        MvGvspPixelType pixelType = null;
        for (MvGvspPixelType value : MvGvspPixelType.values()) {
            if (value.getnValue() == stEnumParam.curValue) {
                pixelType = value;
                break;
            }
        }
        if (pixelType != null) {
            stSaveParam.enPixelType = pixelType;
        }

        // 获取图像帧率
        MVCC_FLOATVALUE stFloatParam = new MVCC_FLOATVALUE();
        nRet = MvCameraControl.MV_CC_GetFloatValue(hCamera, "ResultingFrameRate", stFloatParam);
        if (MV_OK != nRet) {
            logger.error("get ResultingFrameRate value fail!, errcode: [{}]\n", Integer.toHexString(nRet));
            throw new BusinessException("录像失败");
        }
        stSaveParam.fFrameRate = stFloatParam.curValue;

        // ch:录像结构体赋值
        stSaveParam.nBitRate = 1000;
        stSaveParam.enRecordFmtType = MV_FormatType_AVI;
        stSaveParam.strFilePath = filePath + File.separator + String.format("%s.avi", fileName);

        // 开始录像
        nRet = MvCameraControl.MV_CC_StartRecord(hCamera, stSaveParam);
        if (MV_OK != nRet) {
            logger.error("Start Record fail!, errcode: [{}]\n", Integer.toHexString(nRet));
            throw new BusinessException("录像失败");
        }

        // 开始录像
        recordThread.setIsRecording(true);
        recordThread.plusActiveCount();

    }

    /**
     * 停止录像
     *
     * @param ipAddress
     */
    public static void stopSaveVideoByIp(String ipAddress) {

        RecordThread thread = handleMap.get(ipAddress);

        if (thread == null  && thread.gethCamera() != null) {
            logger.error("未找到设备句柄");
            return;
        }
        Handle hCamera = thread.gethCamera();

        int nRet;


        thread.setIsRecording(false);

        // Stop 录像
        nRet = MvCameraControl.MV_CC_StopRecord(hCamera);
        if (MV_OK != nRet) {
            logger.error("StopRecord fail, errcode: [{}]\n", Integer.toHexString(nRet));
        }

        Integer integer = thread.minusActiveCount();
        if (integer == 0) {
            CameraUtils.stopGrabbing(ipAddress);
            CameraUtils.closeDevice(ipAddress);
        }

    }


    /**
     * 拍照
     *
     * @param ipAddress
     * @param fileName
     */
    public static void saveImageByIp(String ipAddress, String fileName) {
        CameraUtils.saveImageByIp(ipAddress, fileName, TEMP_FILE_PATH, SERVER_IP_ADDR);
    }

    /**
     * 拍照
     *
     * @param ipAddress
     * @param fileName
     */
    public static void saveImageByIp(String ipAddress, String fileName, String filePath, String serverIpAddr) {

        RecordThread recordThread = handleMap.get(ipAddress);
        if (recordThread != null && recordThread.gethCamera() != null) {
            recordThread.takePhoto(fileName, filePath);
            return;
        }

        int nRet = MV_OK;
        Handle hCamera = null;

        try {
             MV_CC_DEVICE_INFO ccDeviceInfo = getMvCcDeviceInfo(ipAddress, serverIpAddr);

            // Create handle
            try {
                hCamera = MvCameraControl.MV_CC_CreateHandle(ccDeviceInfo);
            } catch (CameraControlException e) {
                logger.error("Create handle failed!" + e.toString());
                e.printStackTrace();
                hCamera = null;
                throw new BusinessException("连接设备失败");
            }

            // Open device
            nRet = MvCameraControl.MV_CC_OpenDevice(hCamera);
            if (MV_OK != nRet) {
                logger.error("Connect to camera failed, errcode: [{}]\n", Integer.toHexString(nRet));
                throw new BusinessException("连接设备失败");
            }

            // 打开设备了 记录一下
            recordThread = new RecordThread();
            recordThread.sethCamera(hCamera);
            recordThread.plusActiveCount();
            handleMap.put(ipAddress, recordThread);


            // Make sure that trigger mode is off
            nRet = MvCameraControl.MV_CC_SetEnumValueByString(hCamera, "TriggerMode", "Off");
            if (MV_OK != nRet) {
                logger.error("SetTriggerMode failed, errcode: [{}]\n", Integer.toHexString(nRet));
                throw new BusinessException("连接设备失败");
            }

            // Get payload size
            MVCC_INTVALUE stParam = new MVCC_INTVALUE();
            nRet = MvCameraControl.MV_CC_GetIntValue(hCamera, "PayloadSize", stParam);
            if (MV_OK != nRet) {
                logger.error("Get PayloadSize fail, errcode: [{}]\n", Integer.toHexString(nRet));
                throw new BusinessException("连接设备失败");
            }

            // Start grabbing
            nRet = MvCameraControl.MV_CC_StartGrabbing(hCamera);
            if (MV_OK != nRet) {
                logger.error("Start Grabbing fail, errcode: [{}]\n", Integer.toHexString(nRet));
                throw new BusinessException("连接设备失败");
            }

            // Get one frame
            MV_FRAME_OUT_INFO stImageInfo = new MV_FRAME_OUT_INFO();
            byte[] pData = new byte[(int) stParam.curValue];
            nRet = MvCameraControl.MV_CC_GetOneFrameTimeout(hCamera, pData, stImageInfo, 3000);
            if (MV_OK != nRet) {
                logger.error("GetOneFrameTimeout fail, errcode:[{}]\n", Integer.toHexString(nRet));
                throw new BusinessException("连接设备失败");
            }

            System.out.println("GetOneFrame: ");
            printFrameInfo(stImageInfo);
            int imageLen = stImageInfo.width * stImageInfo.height * 3;    // Every RGB pixel takes 3 bytes
            byte[] imageBuffer = new byte[imageLen];

            // Call MV_CC_SaveImage to save image as JPEG
            MV_SAVE_IMAGE_PARAM stSaveParam = new MV_SAVE_IMAGE_PARAM();
            stSaveParam.width = stImageInfo.width;                                  // image width
            stSaveParam.height = stImageInfo.height;                                // image height
            stSaveParam.data = pData;                                               // image data
            stSaveParam.dataLen = stImageInfo.frameLen;                             // image data length
            stSaveParam.pixelType = stImageInfo.pixelType;                          // image pixel format
            stSaveParam.imageBuffer = imageBuffer;                                  // output image buffer
            stSaveParam.imageType = MV_SAVE_IAMGE_TYPE.MV_Image_Jpeg;               // output image pixel format
            stSaveParam.methodValue = 0;                                            // Interpolation method that converts Bayer format to RGB24.  0-Neareast 1-double linear 2-Hamilton
            stSaveParam.jpgQuality = 60;                                            // JPG endoding quality(50-99]

            nRet = MvCameraControl.MV_CC_SaveImage(hCamera, stSaveParam);
            if (MV_OK != nRet) {
                logger.error("SaveImage fail, errcode: [{}]\n", Integer.toHexString(nRet));
                throw new BusinessException("拍照失败！");
            }

            // Save buffer content to file
            saveDataToFile(imageBuffer, stSaveParam.imageLen, String.format("%s.jpeg", fileName), filePath);

            // Stop grabbing
            nRet = MvCameraControl.MV_CC_StopGrabbing(hCamera);
            if (MV_OK != nRet) {
                logger.error("StopGrabbing fail, errcode: [{}]\n", Integer.toHexString(nRet));
                throw new BusinessException("关闭设备失败！");
            }
        } finally {
            if (null != hCamera) {
                // Destroy handle
                nRet = MvCameraControl.MV_CC_DestroyHandle(hCamera);
                if (MV_OK != nRet) {
                    logger.error("DestroyHandle failed, errcode: [{}]\n", Integer.toHexString(nRet));
                }

                if (recordThread != null) {
                    Integer activeCount = recordThread.minusActiveCount();
                    if (activeCount <= 0) {
                        handleMap.remove(ipAddress);
                    }
                }

            }
        }
    }

    private static int chooseCamera(ArrayList<MV_CC_DEVICE_INFO> stDeviceList, String ipAddress) {
        if (null == stDeviceList) {
            return -1;
        }
        int camIndex = -1;
        for (int i = 0; i < stDeviceList.size(); i++) {
            MV_CC_DEVICE_INFO mv_cc_device_info = stDeviceList.get(i);
            if (mv_cc_device_info.transportLayerType == MV_GIGE_DEVICE && mv_cc_device_info.gigEInfo.currentIp.equals(ipAddress)) {
                camIndex = i;
                logger.error("Connect to camera[" + camIndex + "]: " + stDeviceList.get(camIndex).gigEInfo.userDefinedName);
            }
        }
        return camIndex;
    }

    /**
     * 保存图片
     *
     * @param dataToSave
     * @param dataSize
     * @param fileName
     * @param filePath
     */
    public static void saveDataToFile(byte[] dataToSave, int dataSize, String fileName, String filePath) {
        OutputStream os = null;

        try {
            // Create directory
            File tempFile = new File(filePath);
            if (!tempFile.exists()) {
                tempFile.mkdirs();
            }

            os = Files.newOutputStream(Paths.get(tempFile.getPath() + File.separator + fileName));
            os.write(dataToSave, 0, dataSize);
            logger.info("SaveImage succeed.");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // Close file stream
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 打印设备信息
     *
     * @param stDeviceInfo
     */
    private static void printDeviceInfo(MV_CC_DEVICE_INFO stDeviceInfo) {
        if (null == stDeviceInfo) {
            logger.info("stDeviceInfo is null");
            return;
        }

        if (stDeviceInfo.transportLayerType == MV_GIGE_DEVICE) {
            logger.info("\tCurrentIp:       " + stDeviceInfo.gigEInfo.currentIp);
            logger.info("\tModel:           " + stDeviceInfo.gigEInfo.modelName);
            logger.info("\tUserDefinedName: " + stDeviceInfo.gigEInfo.userDefinedName);
        } else if (stDeviceInfo.transportLayerType == MV_USB_DEVICE) {
            logger.info("\tUserDefinedName: " + stDeviceInfo.usb3VInfo.userDefinedName);
            logger.info("\tSerial Number:   " + stDeviceInfo.usb3VInfo.serialNumber);
            logger.info("\tDevice Number:   " + stDeviceInfo.usb3VInfo.deviceNumber);
        } else {
            logger.info("Device is not supported! \n");
        }

        logger.info("\tAccessible:      "
                + MvCameraControl.MV_CC_IsDeviceAccessible(stDeviceInfo, MV_ACCESS_Exclusive));
    }

    /**
     * 打印设备帧数据信息
     *
     * @param stFrameInfo
     */
    private static void printFrameInfo(MV_FRAME_OUT_INFO stFrameInfo) {
        if (null == stFrameInfo) {
            System.err.println("stFrameInfo is null");
            return;
        }

        StringBuilder frameInfo = new StringBuilder("");
        frameInfo.append(("\tFrameNum[" + stFrameInfo.frameNum + "]"));
        frameInfo.append("\tWidth[" + stFrameInfo.width + "]");
        frameInfo.append("\tHeight[" + stFrameInfo.height + "]");
        frameInfo.append(String.format("\tPixelType[%#x]", stFrameInfo.pixelType.getnValue()));

        logger.info(frameInfo.toString());
    }

    /**
     * 图片加水印
     * @param filePath
     * @param imageType
     */
    public static void addWatermark(String filePath, String imageType, String watermark) {
        try {
            // Read the input image
            File inputFile = new File(filePath);
            BufferedImage inputImage = ImageIO.read(inputFile);

            // Create a new image with the same dimensions as the input image
            BufferedImage outputImage = new BufferedImage(inputImage.getWidth(), inputImage.getHeight(), BufferedImage.TYPE_INT_RGB);

            // Get the graphics context of the output image
            Graphics2D g2d = outputImage.createGraphics();
            g2d.drawImage(inputImage, 0, 0, null);

            // Set transparency for watermark text
            AlphaComposite alphaChannel = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f);
            g2d.setComposite(alphaChannel);

            // Set font for watermark text
            Font font = new Font("微软雅黑", Font.PLAIN, 192);
            g2d.setFont(font);

            // Set color for watermark text
            Color color = new Color(255, 0, 0, 255);
            g2d.setColor(color);

            // Get size of text
            FontRenderContext frc = g2d.getFontRenderContext();
            Rectangle2D rect = font.getStringBounds(watermark, frc);
            int textHeight = (int) rect.getHeight() + 20;

            // Add watermark text to image
//            for (int i = -outputImage.getWidth(); i < outputImage.getWidth(); i += textWidth) {
//                for (int j = -outputImage.getHeight(); j < outputImage.getHeight(); j += textHeight) {
//                    AffineTransform at = new AffineTransform();
//                    at.rotate(0, i, j);
//                    g2d.setTransform(at);
//                    g2d.drawString(watermark, i, j);
//                }
//            }

            String[] strings = watermark.split(" ");
            int y = textHeight - 20;
            for (String string : strings) {
                int x = 20;
                g2d.drawString(string, x, y);
                y+= textHeight;
            }


            // Save the output image
            File outputFile = new File(filePath);
            ImageIO.write(outputImage, imageType, outputFile);

            // Dispose graphics resources
            g2d.dispose();

        } catch (IOException e) {
            logger.error("图片加水印报错： {}", e.getMessage(), e);
        }
    }

}
